package com.dheaven.framework.core.dwrmvc;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;
import org.directwebremoting.spring.BeanCreator;
import org.directwebremoting.spring.CreatorConfig;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.ClassUtils;
import org.w3c.dom.Element;

public class DwrAnnotationNamespaceHandler extends NamespaceHandlerSupport {

	protected static final Log log = LogFactory.getLog(DwrAnnotationNamespaceHandler.class);

	public void init() {
		log.info("DwrAnnotationNamespace Handlers.......");
		registerBeanDefinitionParser("url-mapping", new UrlMappingBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
	}

	protected class UrlMappingBeanDefinitionParser implements BeanDefinitionParser {
		public BeanDefinition parse(Element element, ParserContext parserContext) {
			BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DwrHandlerMapping.class);
			parserContext.getRegistry().registerBeanDefinition("DwrAnnotationURLMapper", builder.getBeanDefinition());
			return parserContext.getRegistry().getBeanDefinition("DwrAnnotationURLMapper");
		}

	}

	protected class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {
		public BeanDefinition parse(Element element, ParserContext parserContext) {
			BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry();
			for (String beanName : beanDefinitionRegistry.getBeanDefinitionNames()) {
				BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinitionRegistry.getBeanDefinition(beanName), beanName);
				Class<?> beanDefinitionClass = getBeanDefinitionClass(beanDefinitionHolder, beanDefinitionRegistry);
				RemoteProxy annotation = beanDefinitionClass.getAnnotation(RemoteProxy.class);
				if (annotation != null) {
					String javascript = annotation.name();
					log.info("DWR: Detected candidate bean [" + beanName + "]. Remoting using " + javascript);
					registerCreator(beanDefinitionHolder, beanDefinitionRegistry, beanDefinitionClass, javascript);
				}
			}
			return null;
		}

		protected Class<?> getBeanDefinitionClass(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry beanDefinitionRegistry) {
			String beanClassName = SharedCode.resolveBeanClassname(beanDefinitionHolder.getBeanDefinition(), beanDefinitionRegistry);
			try {
				if (beanClassName == null) {
					throw new FatalBeanException("Unabled to find type for beanName '" + beanDefinitionHolder.getBeanName() + "'. " + "Check your bean has a correctly configured parent or provide a class for " + " the bean definition");
				}
				return ClassUtils.forName(beanClassName);
			} catch (ClassNotFoundException cne) {
				throw new FatalBeanException("Unable to create DWR bean creator for '" + beanDefinitionHolder.getBeanName() + "'.", cne);
			}
		}

	}

	protected void registerCreator(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry beanDefinitionRegistry, Class<?> beanDefinitionClass, String javascript) {
		BeanDefinitionBuilder beanCreator = BeanDefinitionBuilder.rootBeanDefinition(BeanCreator.class);
		try {
			beanCreator.addPropertyValue("beanClass", beanDefinitionClass);
			beanCreator.addPropertyValue("beanId", beanDefinitionHolder.getBeanName());
			String creatorConfigName = "__" + javascript;
			beanCreator.addPropertyValue("javascript", javascript);
			BeanDefinitionBuilder creatorConfig = BeanDefinitionBuilder.rootBeanDefinition(CreatorConfig.class);
			creatorConfig.addPropertyValue("creator", beanCreator.getBeanDefinition());
			List<String> includes = new ArrayList<String>();
			for (Method method : beanDefinitionClass.getMethods()) {
				if (method.getAnnotation(RemoteMethod.class) != null) {
					includes.add(method.getName());
				}
			}
			creatorConfig.addPropertyValue("includes", includes);
			BeanDefinitionHolder aux = new BeanDefinitionHolder(creatorConfig.getBeanDefinition(), creatorConfigName);
			BeanDefinitionReaderUtils.registerBeanDefinition(aux, beanDefinitionRegistry);
			SharedCode.lookupCreators(beanDefinitionRegistry).put(javascript, new RuntimeBeanReference(creatorConfigName));
		} catch (Exception ex) {
			throw new FatalBeanException("Unable to create DWR bean creator for '" + beanDefinitionHolder.getBeanName() + "'. ", ex);
		}
	}

}
